# frozen_string_literal: true

require 'spec_helper'

RSpec.shared_examples 'Signup name validation' do |field, max_length, label|
  flag_values = [true, false]
  flag_values.each do |val|
    before do
      stub_feature_flags(restyle_login_page: val)
      visit new_user_registration_path
    end

    describe "#{field} validation" do
      it "does not show an error border if the user's fullname length is not longer than #{max_length} characters" do
        fill_in field, with: 'u' * max_length

        expect(find('.name')).not_to have_css '.gl-field-error-outline'
      end

      it 'shows an error border if the user\'s fullname contains an emoji' do
        simulate_input("##{field}", 'Ehsan 🦋')

        expect(find('.name')).to have_css '.gl-field-error-outline'
      end

      it "shows an error border if the user\'s fullname is longer than #{max_length} characters" do
        fill_in field, with: 'n' * (max_length + 1)

        expect(find('.name')).to have_css '.gl-field-error-outline'
      end

      it "shows an error message if the user\'s #{label} is longer than #{max_length} characters" do
        fill_in field, with: 'n' * (max_length + 1)

        expect(page).to have_content("#{label} is too long (maximum is #{max_length} characters).")
      end

      it 'shows an error message if the username contains emojis' do
        simulate_input("##{field}", 'Ehsan 🦋')

        expect(page).to have_content("Invalid input, please avoid emojis")
      end
    end
  end
end

RSpec.describe 'Signup', :js, feature_category: :user_profile do
  include TermsHelper

  let(:new_user) { build_stubbed(:user) }

  def fill_in_signup_form
    fill_in 'new_user_username', with: new_user.username
    fill_in 'new_user_email', with: new_user.email
    fill_in 'new_user_first_name', with: new_user.first_name
    fill_in 'new_user_last_name', with: new_user.last_name
    fill_in 'new_user_password', with: new_user.password
  end

  def confirm_email
    new_user_token = User.find_by_email(new_user.email).confirmation_token

    visit user_confirmation_path(confirmation_token: new_user_token)
  end

  flag_values = [true, false]
  flag_values.each do |val|
    before do
      stub_feature_flags(arkose_labs_signup_challenge: false)
      stub_feature_flags(restyle_login_page: val)
      stub_application_setting(require_admin_approval_after_user_signup: false)
    end

    describe 'username validation' do
      before do
        visit new_user_registration_path
      end

      it 'does not show an error border if the username is available' do
        fill_in 'new_user_username', with: 'new-user'
        wait_for_requests

        expect(find('.username')).not_to have_css '.gl-field-error-outline'
      end

      it 'does not show an error border if the username contains dots (.)' do
        simulate_input('#new_user_username', 'new.user.username')
        wait_for_requests

        expect(find('.username')).not_to have_css '.gl-field-error-outline'
      end

      it 'does not show an error border if the username length is not longer than 255 characters' do
        fill_in 'new_user_username', with: 'u' * 255
        wait_for_requests

        expect(find('.username')).not_to have_css '.gl-field-error-outline'
      end

      it 'shows an error border if the username already exists' do
        existing_user = create(:user)

        fill_in 'new_user_username', with: existing_user.username
        wait_for_requests

        expect(find('.username')).to have_css '.gl-field-error-outline'
      end

      it 'shows a success border if the username is available' do
        fill_in 'new_user_username', with: 'new-user'
        wait_for_requests

        expect(find('.username')).to have_css '.gl-field-success-outline'
      end

      it 'shows an error border if the username contains special characters' do
        fill_in 'new_user_username', with: 'new$user!username'
        wait_for_requests

        expect(find('.username')).to have_css '.gl-field-error-outline'
      end

      it 'shows an error border if the username is longer than 255 characters' do
        fill_in 'new_user_username', with: 'u' * 256
        wait_for_requests

        expect(find('.username')).to have_css '.gl-field-error-outline'
      end

      it 'shows an error message if the username is longer than 255 characters' do
        fill_in 'new_user_username', with: 'u' * 256
        wait_for_requests

        expect(page).to have_content("Username is too long (maximum is 255 characters).")
      end

      it 'shows an error message if the username is less than 2 characters' do
        fill_in 'new_user_username', with: 'u'
        wait_for_requests

        expect(page).to have_content("Username is too short (minimum is 2 characters).")
      end

      it 'shows an error message on submit if the username contains special characters' do
        fill_in 'new_user_username', with: 'new$user!username'
        wait_for_requests

        click_button "Register"

        expect(page).to have_content("Please create a username with only alphanumeric characters.")
      end

      it 'shows an error border if the username contains emojis' do
        simulate_input('#new_user_username', 'ehsan😀')

        expect(find('.username')).to have_css '.gl-field-error-outline'
      end

      it 'shows an error message if the username contains emojis' do
        simulate_input('#new_user_username', 'ehsan😀')

        expect(page).to have_content("Invalid input, please avoid emojis")
      end

      it 'shows a pending message if the username availability is being fetched', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/31484' do
        fill_in 'new_user_username', with: 'new-user'

        expect(find('.username > .validation-pending')).not_to have_css '.hide'
      end

      it 'shows a success message if the username is available' do
        fill_in 'new_user_username', with: 'new-user'
        wait_for_requests

        expect(find('.username > .validation-success')).not_to have_css '.hide'
      end

      it 'shows an error message if the username is unavailable' do
        existing_user = create(:user)

        fill_in 'new_user_username', with: existing_user.username
        wait_for_requests

        expect(find('.username > .validation-error')).not_to have_css '.hide'
      end

      it 'shows a success message if the username is corrected and then available' do
        fill_in 'new_user_username', with: 'new-user$'
        wait_for_requests
        fill_in 'new_user_username', with: 'new-user'
        wait_for_requests

        expect(page).to have_content("Username is available.")
      end
    end

    context 'with no errors' do
      context 'when sending confirmation email' do
        before do
          stub_application_setting_enum('email_confirmation_setting', 'hard')
        end

        context 'when email confirmation setting is not `soft`' do
          before do
            stub_feature_flags(identity_verification: false)
          end

          it 'creates the user account and sends a confirmation email, and pre-fills email address after confirming' do
            visit new_user_registration_path

            fill_in_signup_form

            expect { click_button 'Register' }.to change { User.count }.by(1)
            expect(page).to have_current_path users_almost_there_path, ignore_query: true
            expect(page).to have_content("Please check your email (#{new_user.email}) to confirm your account")

            confirm_email

            expect(find_field('Username or email').value).to eq(new_user.email)
          end
        end

        context 'when email confirmation setting is `soft`' do
          before do
            stub_application_setting_enum('email_confirmation_setting', 'soft')
          end

          it 'creates the user account and sends a confirmation email' do
            visit new_user_registration_path

            fill_in_signup_form

            expect { click_button 'Register' }.to change { User.count }.by(1)
            expect(page).to have_current_path users_sign_up_welcome_path, ignore_query: true
          end
        end
      end

      context "when not sending confirmation email" do
        before do
          stub_application_setting_enum('email_confirmation_setting', 'off')
        end

        it 'creates the user account and goes to dashboard' do
          visit new_user_registration_path

          fill_in_signup_form
          click_button "Register"

          expect(page).to have_current_path users_sign_up_welcome_path, ignore_query: true
        end
      end

      context 'with required admin approval enabled' do
        before do
          stub_application_setting(require_admin_approval_after_user_signup: true)
        end

        it 'creates the user but does not sign them in' do
          visit new_user_registration_path

          fill_in_signup_form

          expect { click_button 'Register' }.to change { User.count }.by(1)
          expect(page).to have_current_path new_user_session_path, ignore_query: true
          expect(page).to have_content("You have signed up successfully. However, we could not sign you in because your account is awaiting approval from your GitLab administrator")
        end
      end
    end

    context 'with errors' do
      it "displays the errors" do
        create(:user, email: new_user.email)
        visit new_user_registration_path

        fill_in_signup_form
        click_button "Register"

        expect(page).to have_current_path user_registration_path, ignore_query: true
        expect(page).to have_content("error prohibited this user from being saved")
        expect(page).to have_content("Email has already been taken")
      end

      it 'redisplays all fields except password' do
        create(:user, email: new_user.email)
        visit new_user_registration_path

        fill_in_signup_form
        click_button "Register"

        expect(page).to have_current_path user_registration_path, ignore_query: true
        expect(page.body).not_to match(/#{new_user.password}/)

        expect(find_field('First name').value).to eq(new_user.first_name)
        expect(find_field('Last name').value).to eq(new_user.last_name)
        expect(find_field('Username').value).to eq(new_user.username)
        expect(find_field('Email').value).to eq(new_user.email)
      end
    end

    context 'when terms are enforced' do
      before do
        enforce_terms
      end

      it 'renders text that the user confirms terms by signing in' do
        visit new_user_registration_path
        expect(page).to have_content(/By clicking Register, I agree that I have read and accepted the Terms of Use and Privacy Policy/)

        fill_in_signup_form
        click_button 'Register'

        expect(page).to have_current_path users_sign_up_welcome_path, ignore_query: true
      end
    end

    context 'when reCAPTCHA and invisible captcha are enabled' do
      before do
        stub_application_setting(invisible_captcha_enabled: true)
        stub_application_setting(recaptcha_enabled: true)
        allow_next_instance_of(RegistrationsController) do |instance|
          allow(instance).to receive(:verify_recaptcha).and_return(true)
        end
      end

      context 'when reCAPTCHA detects malicious behaviour' do
        before do
          allow_next_instance_of(RegistrationsController) do |instance|
            allow(instance).to receive(:verify_recaptcha).and_return(false)
          end
        end

        it 'prevents from signing up' do
          visit new_user_registration_path

          fill_in_signup_form

          expect { click_button 'Register' }.not_to change { User.count }
          expect(page).to have_content(_('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'))
          expect(page).to have_content("Minimum length is #{Gitlab::CurrentSettings.minimum_password_length} characters")
        end
      end

      context 'when invisible captcha detects malicious behaviour' do
        it 'prevents from signing up' do
          visit new_user_registration_path

          fill_in_signup_form

          expect { click_button 'Register' }.not_to change { User.count }
          expect(page).to have_content('That was a bit too quick! Please resubmit.')
        end
      end
    end

    it 'redirects to step 2 of the signup process, sets the role and redirects back' do
      visit new_user_registration_path

      fill_in_signup_form
      wait_for_all_requests

      click_button 'Register'

      visit new_project_path

      expect(page).to have_current_path(users_sign_up_welcome_path)

      select 'Software Developer', from: 'user_role'
      click_button 'Get started!'

      created_user = User.find_by_username(new_user.username)

      expect(created_user.software_developer_role?).to be_truthy
      expect(created_user.setup_for_company).to be_nil
      expect(page).to have_current_path(new_project_path)
    end

    it 'does not redisplay the password' do
      create(:user, email: new_user.email)
      visit new_user_registration_path

      fill_in_signup_form
      click_button "Register"

      expect(page).to have_current_path user_registration_path, ignore_query: true
      expect(page.body).not_to match(/#{new_user.password}/)
    end

    context 'with invalid email' do
      it_behaves_like 'user email validation' do
        let(:path) { new_user_registration_path }
      end
    end
  end

  context 'when terms are enforced' do
    before do
      enforce_terms
    end

    it 'renders text that the user confirms terms by signing in' do
      visit new_user_registration_path

      expect(page).to have_content(/By clicking Register, I agree that I have read and accepted the Terms of Use and Privacy Policy/)

      fill_in_signup_form
      click_button 'Register'

      visit new_project_path

      expect(page).to have_current_path(users_sign_up_welcome_path)

      select 'Software Developer', from: 'user_role'
      click_button 'Get started!'

      created_user = User.find_by_username(new_user.username)

      expect(created_user.software_developer_role?).to be_truthy
      expect(created_user.setup_for_company).to be_nil
      expect(page).to have_current_path(new_project_path)
    end

    it_behaves_like 'Signup name validation', 'new_user_first_name', 127, 'First name'
    it_behaves_like 'Signup name validation', 'new_user_last_name', 127, 'Last name'
  end
end
